<?php

namespace app\server\controller;

class Ws
{
    public $socket = null;
    public $read_sockets = [];
    public $write_socket = null;
    public $except_socket = null;
    public $users = [];

    public function __construct($ip,$port)
    {
        $this->socket = socket_create(AF_INET,SOCK_STREAM,SOL_TCP);
        socket_set_option($this->socket,SOL_SOCKET,SO_REUSEADDR,true);
        socket_bind($this->socket,$ip,$port);
        socket_listen($this->socket);

        $this->read_sockets[] = $this->socket;
        var_dump($this->read_sockets);

        while (true){
            $tmp_sockets = $this->read_sockets;
            socket_select($tmp_sockets,$this->write_socket,$this->except_socket,null);
            foreach ($tmp_sockets as $sock){
                if($sock == $this->socket){
                   $conSock = socket_accept($this->socket);
                    $this->read_sockets[] = $conSock;
                   $this->users[] = ['socket'=>$conSock,'handshake'=>false];
                }else{
                    $request = socket_read($sock,1024);
                    $k = $this->getUserIndex($sock);

                    //关闭消息
                    if(strlen($request)==8){
                        $this->close($k);
                        continue;
                    }

                    if(!$this->users[$k]['handshake']){
                        //服务器与客户端建立连接
                        $response = $this->handleShake($request);
                        socket_write($sock,$response,strlen($response));
                        $this->users[$k]['handshake'] = true;
                    }else{
                        $msg = $this->decode($request);
                        if(strlen($msg)>0){
                            $this->send($msg,$k);
                            echo "====服务端===发送消息===$msg".PHP_EOL;
                        }
                    }
                }
            }
        }
    }

    private function send($msg,$k)
    {
        $arr = explode("===",$msg);
        if($arr[0]=='login'){
            $this->users[$k]['name'] = $arr[1];

            $res['msg'] = $arr[1].':login success';
            $res['type'] = 'login';

            $names['name'] = $this->getUserName();
            $names['type'] = 'user';

            $names = $this->encode(json_encode($names));
            foreach($this->users as $v){
                socket_write($v['socket'],$names,strlen($names));
            }
        }

        if ($arr[0]=='con'){
            $res['content'] = $arr[1];
            $res['name'] = $this->users[$k]['name'];
            $res['time'] = date('Y-m-d H:i:s',time());
            $res['type'] ='con';
        }

        $res = $this->encode(json_encode($res));
        if($res){
            foreach ($this->users as $v){
                socket_write($v['socket'],$res,strlen($res));
            }
        }
    }

    //关闭客户端
    private function close($k){
        socket_close($this->users[$k]["socket"]);
        unset($this->users[$k]);
        $this->read_sockets = [];
        $this->read_sockets[] = $this->socket;
        foreach ($this->users as $v){
            $this->read_sockets[] = $v['socket'];
        }
    }

    private function getUserName(){
        foreach($this->users as $v){
            $name[] = $v['name'];
        }
        return $name;

    }

    private function getUserIndex($sock)
    {
        foreach ($this->users as $k=>$v)
        {
            if ($v['socket'] == $sock){
               return $k;
            }
        }
    }

    private function encode($msg) {
        $frame = [];
        $frame[0] = '81';
        $len = strlen($msg);
        if ($len < 126) {
            $frame[1] = $len < 16 ? '0' . dechex($len) : dechex($len);
        } else if ($len < 65025) {
            $s = dechex($len);
            $frame[1] = '7e' . str_repeat('0', 4 - strlen($s)) . $s;
        } else {
            $s = dechex($len);
            $frame[1] = '7f' . str_repeat('0', 16 - strlen($s)) . $s;
        }
        $data = '';
        $l = strlen($msg);
        for ($i = 0; $i < $l; $i++) {
            $data .= dechex(ord($msg[$i]));
        }
        $frame[2] = $data;
        $data = implode('', $frame);
        return pack("H*", $data);
    }



    private function handleShake($request){
        preg_match("/Sec-WebSocket-Key: (.*)\r\n/",$request,$match);
        $key = $match[1];
        echo "==客户端的key===$key==".PHP_EOL;
        $new_key = base64_encode(sha1($key.'258EAFA5-E914-47DA-95CA-C5AB0DC85B11',true));
        $response = "HTTP/1.1 101 Switching Protocols\r\n";
        $response .= "Upgrade: websocket\r\n";
        $response .= "Connection: Upgrade\r\n";
        $response .= "Sec-WebSocket-Accept: $new_key\r\n";
        $response .= "\r\n";
        return $response;
    }

    private function decode($buffer) {
        $decoded = '';
        $len = ord($buffer[1]) & 127;
        if ($len === 126) {
            $masks = substr($buffer, 4, 4);
            $data = substr($buffer, 8);
        } else if ($len === 127) {
            $masks = substr($buffer, 10, 4);
            $data = substr($buffer, 14);
        } else {
            $masks = substr($buffer, 2, 4);
            $data = substr($buffer, 6);
        }
        for ($index = 0; $index < strlen($data); $index++) {
            $decoded .= $data[$index] ^ $masks[$index % 4];
        }
        return $decoded;
    }
}
new Ws("0.0.0.0",9999);

